package com.feng.config;

import com.alibaba.fastjson2.JSONObject;
import com.feng.entity.Account;
import com.feng.entity.RestResponse;
import com.feng.entity.User;
import com.feng.entity.vo.AccountLoginVo;
import com.feng.filter.AccessDeniedFilter;
import com.feng.filter.AuthenticationFilter;
import com.feng.filter.JwtAuthorizeFilter;
import com.feng.utils.JwtUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Configuration
public class SecurityConfiguration {

    @Autowired
    JwtUtil jwtUtil;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    JwtAuthorizeFilter jwtAuthorizeFilter;

    @Autowired
    AccessDeniedFilter accessDeniedFilter;

    @Autowired
    AuthenticationFilter authenticationFilter;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests( conf -> conf
                        .requestMatchers("/api/auth/**", "/swagger-ui/**", "/profile/**", "/ai/**",
                                "/chatRoom/**").permitAll()
                        .anyRequest().authenticated())
                .formLogin( conf -> conf
                        .loginProcessingUrl("/api/auth/login")
                        .successHandler(this::onAuthenticationSuccess)
                        .failureHandler(this::onAuthenticationFailure)
                )
                .logout( conf -> conf
                        .logoutUrl("/api/auth/logout")
                        .logoutSuccessHandler(this::onLogoutSuccess))
                .sessionManagement( conf -> conf
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling(conf ->
                    conf.accessDeniedHandler(accessDeniedFilter)
                            .authenticationEntryPoint(authenticationFilter)
                )
                .addFilterBefore(jwtAuthorizeFilter, UsernamePasswordAuthenticationFilter.class)
                .csrf(AbstractHttpConfigurer::disable)
                .build();
    }

    //当登录成功时执行的方法
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        response.setCharacterEncoding("utf-8");
        User user = (User)authentication.getPrincipal();
        String token = jwtUtil.createToken(user);
        Date expireTime = jwtUtil.getExpireTime(token);
        Duration duration = Duration.between(Instant.now(), expireTime.toInstant());
        long millis = duration.toMillis();
        try{
            redisTemplate.opsForValue().set("login:" + user.getAccount().getId(), JSONObject.toJSONString(user),
                    millis, TimeUnit.MILLISECONDS);
            redisTemplate.opsForValue().set("token:" + user.getAccount().getId(), token,
                    millis, TimeUnit.MILLISECONDS);
        }catch (Exception e){
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                response.getWriter().write(RestResponse.failure(500,"Redis操作失败").asJsonString());
            return;
        }
        AccountLoginVo loginVo = new AccountLoginVo(token,
                expireTime);
        response.getWriter().write(RestResponse.success(loginVo).asJsonString());
    }

    //当登录失败时执行的方法
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {
        response.setCharacterEncoding("utf-8");
        response.getWriter().write(RestResponse.failure(401, exception.getMessage()).asJsonString());
    }

    //当退出登录成功时执行的方法
    public void onLogoutSuccess(HttpServletRequest request,
                                HttpServletResponse response,
                                Authentication authentication) throws IOException, ServletException {
        response.setCharacterEncoding("utf-8");
        String token = request.getHeader("Authorization");
        if(token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);
            String id = jwtUtil.verifyToken(token);
            if (id != null) {
                User user = JSONObject.parseObject(redisTemplate.opsForValue().get("login:" + id), User.class);
                if(user != null){
                    redisTemplate.delete("login:" + user.getAccount().getId());
                    redisTemplate.delete("token:" + user.getAccount().getId());
                    response.getWriter().write(RestResponse.success().asJsonString());
                    return;
                }
            }
        }
        response.getWriter().write(RestResponse.success().asJsonString());
    }

}
